目录
  1. 1. 公式推导
  2. 2. 最终代码
  3. 3. 向量化技术⭐️
    1. 3.1. 1. 改造公式
    2. 3.2. 2. 变量维度说明:
    3. 3.3. 3. 列向量改矩阵⭐️
  4. 4. 对于一些细节的补充:
Matlab编程-- 向量化技术&似然概率

Matlab编程-- 向量化技术&似然概率

本篇博客的目标:对数似然概率公式的Matlab编程

对数似然概率公式:

ln⁡p(X∣μ,Σ)=−12(Dln(2π)+ln∣Σ∣+(xn−μ)TΣ−1(xn−μ))\begin{aligned} \ln p(\mathbf{X} | \boldsymbol{\mu}, \mathbf{\Sigma})= -\frac{1}{2}(Dln(2\pi)+ln|\Sigma|+\left(\mathbf{x}_{\mathbf{n}}-\boldsymbol{\mu}\right)^{T} \mathbf{\Sigma}^{-1}\left(\mathbf{x}_{\mathbf{n}}-\boldsymbol{\mu}\right)) \end{aligned}

编程难点:公式中的xx是列向量,而Matlab中的XX是矩阵,为了提高代码的效率避免使用过多的forfor语句,这里使用向量化技术完成算法的编程任务。

公式推导

参考链接:https://www.cnblogs.com/ccienfall/p/6049021.html

多维高斯函数的似然概率:

N(x∣μ,Σ)=1(2π)D/21∣Σ∣1/2exp⁡{−12(x−μ)TΣ−1(x−μ)}\mathcal{N}(\mathbf{x} | \boldsymbol{\mu}, \mathbf{\Sigma})=\frac{1}{(2 \pi)^{D / 2}} \frac{1}{|\mathbf{\Sigma}|^{1 / 2}} \exp \left\{-\frac{1}{2}(\mathbf{x}-\boldsymbol{\mu})^{T} \mathbf{\Sigma}^{-1}(\mathbf{x}-\boldsymbol{\mu})\right\}

若是考虑数据集X=(x1,…,xN)T\mathbf{X}=\left(\mathbf{x}_{1}, \ldots, \mathbf{x}_{N}\right)^{T},再求对数则为:

ln⁡p(X∣μ,Σ)=−ND2ln⁡(2π)−N2ln⁡∣Σ∣−12∑n=1N(xn−μ)TΣ−1(xn−μ)\ln p(\mathbf{X} | \boldsymbol{\mu}, \mathbf{\Sigma})=-\frac{N D}{2} \ln (2 \pi)-\frac{N}{2} \ln |\mathbf{\Sigma}|-\frac{1}{2} \sum_{n=1}^{N}\left(\mathbf{x}_{\mathbf{n}}-\boldsymbol{\mu}\right)^{T} \mathbf{\Sigma}^{-1}\left(\mathbf{x}_{\mathbf{n}}-\boldsymbol{\mu}\right)

⭐️但是我需要的似然概率,xix_i在第kk个分布下的似然概率:【实数】

N(xi∣μk,Σk)=1(2π)D/21∣Σk∣1/2exp⁡{−12(xi−μk)TΣk−1(xi−μk)}\mathcal{N}(\mathbf{x_i} | \boldsymbol{\mu_k}, \mathbf{\Sigma_k})=\frac{1}{(2 \pi)^{D / 2}} \frac{1}{|\mathbf{\Sigma_k}|^{1 / 2}} \exp \left\{-\frac{1}{2}(\mathbf{x_i}-\boldsymbol{\mu_k})^{T} \mathbf{\Sigma_k}^{-1}(\mathbf{x_i}-\boldsymbol{\mu_k})\right\}

展开后,得证:

ln⁡p(X∣μ,Σ)=−D2ln⁡(2π)−12ln⁡∣Σ∣−12(xn−μ)TΣ−1(xn−μ)=−12(Dln(2π)+ln∣Σ∣+(xn−μ)TΣ−1(xn−μ))\begin{aligned} \ln p(\mathbf{X} | \boldsymbol{\mu}, \mathbf{\Sigma})= &-\frac{ D}{2} \ln (2 \pi)-\frac{1}{2} \ln |\mathbf{\Sigma}|-\frac{1}{2} \left(\mathbf{x}_{\mathbf{n}}-\boldsymbol{\mu}\right)^{T} \mathbf{\Sigma}^{-1}\left(\mathbf{x}_{\mathbf{n}}-\boldsymbol{\mu}\right) \\ = & -\frac{1}{2}(Dln(2\pi)+ln|\Sigma|+\left(\mathbf{x}_{\mathbf{n}}-\boldsymbol{\mu}\right)^{T} \mathbf{\Sigma}^{-1}\left(\mathbf{x}_{\mathbf{n}}-\boldsymbol{\mu}\right)) \end{aligned}

最终代码

function logprob = lgmmprob(data, mu, sigma, w)
% 计算GMM的观察概率(似然概率)
ndim = size(data, 1);
C = sum(mu.*mu./sigma) + sum(log(sigma));
D = (1./sigma)' * (data .* data) - 2 * (mu./sigma)' * data + ndim * log(2 * pi);
logprob = -0.5 * (bsxfun(@plus, C', D));
logprob = bsxfun(@plus, logprob, log(w));

舍弃系数-0.5,进一步简化需要编程的部分:

Dln(2π)+ln∣Σ∣+(xi−μk)TΣk−1(xi−μk)Dln(2\pi)+ln|\Sigma|+\left(\mathbf{x}_i-\boldsymbol{\mu_k}\right)^{T} \mathbf{\Sigma_k}^{-1}\left(\mathbf{x}_i-\boldsymbol{\mu_k}\right)

需要说明的是上面所有的小项都是实数。

向量化技术⭐️

1. 改造公式

Σ\Sigma在数学推导中的公式中是 full−variancefull -variance 矩阵,但是一般代码中的协方差矩阵默认是对角协方差矩阵。故这里就存在一个算法和实际编程中的一个不匹配的地方,故这个时候需要对原本的算法公式进行改造

  • 正常的推到方程

(a1,a2)∗(Δ100Δ2)∗(a1a2)=Δ1a12+Δ2a22\left(a_{1}, a_{2}\right)*\left(\begin{array}{cc} {\Delta_{1}} & {0} \\ {0} & {\Delta_{2}} \end{array}\right)*\left(\begin{array}{l} {a_{1}} \\ {a_{2}} \end{array}\right)=\Delta_{1} a_{1}^{2}+\Delta_{2} a_{2}^{2}

  • 代码中待编程的方程

(Δ1,Δ2)∗(a1a2).∗(a1a2)=Δ1a12+Δ2a22\left(\Delta_{1}, \Delta_{2}\right)*\left(\begin{array}{l} {a_{1}} \\ {a_{2}} \end{array}\right).*\left(\begin{array}{l} {a_{1}} \\ {a_{2}} \end{array}\right)=\Delta_{1} a_{1}^{2}+\Delta_{2} a_{2}^{2}

故原先的第三项为:(xi−μk)TΣk−1(xi−μk)\left(\mathbf{x}_i-\boldsymbol{\mu_k}\right)^{T} \mathbf{\Sigma_k}^{-1}\left(\mathbf{x}_i-\boldsymbol{\mu_k}\right)

其中令Σ\Sigma是一个列向量,可以见下节的变量维度说明。(Δ1,Δ2)=(Σk−1)T(\Delta_1,\Delta_2)=(\Sigma_{k}^{-1})^T

(Σk−1)T∗(xi−μk).∗(xi−μk)(\mathbf{\Sigma_k}^{-1})^T*\left(\mathbf{x}_i-\boldsymbol{\mu_k}\right).*\left(\mathbf{x}_i-\boldsymbol{\mu_k}\right)

  • 本例中,代码所需编写的方程为:

Dln(2π)+ln∣Σk∣+(Σk−1)T∗[xi .∗ xi−2μk .∗ xi+μk .∗ μk]Dln(2\pi)+ln|\Sigma_k|+(\mathbf{\Sigma_k}^{-1})^T*[x_i\ .*\ x_i-2\mu_k\ .*\ x_i+\mu_k\ .*\ \mu_k]

2. 变量维度说明:

xi:(p,1)x_i:(p,1)

X={x1,x2,…,xi,…,xn}:(p,n)X=\{x_1,x_2,\dots,x_i,\dots,x_n\}:(p,n)

Σk:(p,1)\Sigma_k:(p,1)

Σ={Σ1,Σ2,…,Σk,…,ΣK}:(p,K)\Sigma = \{\Sigma_1,\Sigma_2,\dots,\Sigma_k,\dots,\Sigma_K\}:(p,K)

μk:(p,1)\mu_k:(p,1)

μ={μ1,μ2,…,μk,…,μK}:(p,K)\mu=\{\mu_1,\mu_2,\dots,\mu_k,\dots,\mu_K\}:(p,K)

3. 列向量改矩阵⭐️

这个是最关键的一步,一步一步解答,以便下次遇到的时候可以轻松应对,上式是原数学方程式中的数学向量,但为了编程,需要向量化。

  • 首先明确向量化的目的:用Σ,μ,X\Sigma,\mu,X代替Σk,μk,xi\Sigma_k,\mu_k,x_i
  • 其次明确维度:每一项的都是实数

这里再明确一下待编程的方程为:

Dln(2π)+ln∣Σk∣+(Σk−1)T∗[xi .∗ xi−2μk .∗ xi+μk .∗ μk]Dln(2\pi)+ln|\Sigma_k|+(\mathbf{\Sigma_k}^{-1})^T*[x_i\ .*\ x_i-2\mu_k\ .*\ x_i+\mu_k\ .*\ \mu_k]

根据每一小项是否与分布kk和样本ii有关可进一步划分:

与k有关 与k无关
与ii有关 1. k与i 矩阵乘
2. k 与 i 点乘,需转换为矩阵乘
3. 不满足以上两个条件
与ii无关 ln∥Σk−1∥ln\|\Sigma_k^{-1}\| Dln(2π)Dln(2\pi)
  1. 情况一:与kk和ii都无关的项,Dln(2π):Dln(2\pi):实数
ndim * log(2 * pi)
  1. 情况二:仅与kk有关的项,ln∣Σk−1∣:ln|\Sigma_k^{-1}|: 实数

    这里试求方差的行列式,又方差是对角矩阵,所以可简化为:

ln∣(Δ100Δ2)∣=ln∣Δ1⋅Δ2∣=lnΔ1+lnΔ2=sum(ln(Δ1,Δ2))ln|\left(\begin{array}{cc} {\Delta_{1}} & {0} \\ {0} & {\Delta_{2}} \end{array}\right)|=ln|\Delta_1\cdot\Delta_2|=ln\Delta_1+ln\Delta_2=sum(ln(\Delta_1,\Delta_2))

​ ln∣Σk∣=sum(Σk):(1,1)ln|\Sigma_k|=sum(\Sigma_k):(1,1):

​ 可以向右扩展:

​ {sum(Σ1),sum(Σ2),…,sum(Σk)}=sum(Σ):(1,k)\{sum(\Sigma_1),sum(\Sigma_2),\dots,sum(\Sigma_k)\}=sum(\Sigma):(1,k)

  1. 情况三:kk与ii已经分开,且由矩阵乘相连

    (Σk−1)T∗[xi .∗ xi](\mathbf{\Sigma_k}^{-1})^T*[x_i\ .*\ x_i]:(p,1)T∗(p,1).∗(p,1)=(1,1)(p,1)^T*(p,1).*(p,1)=(1,1)实数

    向量化具体细节讲解:

    元素向量化:将xix_i横向扩展,将Σk\Sigma_k纵向扩展

{(Σk−1)Tx1.∗x1,(Σk−1)Tx2.∗x2,…,(Σk−1)Txn.∗xn}=(Σk−1)T∗{x1.∗x1, x2.∗x2, …,xn.∗xn}=(Σk−1)T∗(X.∗X):(1,p)∗(p,n)=(1,n)还可以向上下扩展=((Σ1−1,Σ2−1,…,ΣK−1))T∗(X.∗X)=(Σ−1)T∗(X.∗X):(k,n)\{(\mathbf{\Sigma_k}^{-1})^Tx_1.*x_1,(\mathbf{\Sigma_k}^{-1})^Tx_2.*x_2,\dots,(\mathbf{\Sigma_k}^{-1})^Tx_n.*x_n\} \\ = (\mathbf{\Sigma_k}^{-1})^T *\{x_1.*x_1,\ x_2.*x_2,\ \dots,x_n.*x_n\} \\ =(\mathbf{\Sigma_k}^{-1})^T*(X.*X):(1,p)*(p,n)=(1,n)还可以向上下扩展\\ =((\mathbf{\Sigma_1}^{-1},\mathbf{\Sigma_2}^{-1},\dots,\mathbf{\Sigma_K}^{-1}))^T*(X.*X) \\=(\Sigma^{-1})^T*(X.*X):(k,n)

  1. 情况四:kk和ii为点乘,需要转换为矩阵乘:(Σk−1)T∗[−2μk.∗xi](\mathbf{\Sigma_k}^{-1})^T*[-2\mu_k.*x_i]:(p,1)T∗(p,1)=(1,1)(p,1)^T*(p,1)=(1,1)实数

但此时有个问题,无法进行向量化,因为这里的运算规则必须先进行点乘运算,再进行矩阵乘运算,若需向量化中间必须用矩阵乘相连,原因后面解释。

−2∗(Σk−1)T.∗μkT∗xi=−2(Σk−1.∗μk)T∗xi-2*(\Sigma_k^{-1})^T.*\mu_k^T*x_i=-2(\Sigma_k^{-1}.*\mu_k)^T*x_i

​ 向量化处理同情况三:直接去掉下标即可

−2(Σ−1.∗μ)T∗X-2(\Sigma^{-1}.*\mu)^T*X

  1. 情况五:都为kk,中间既有矩阵乘又有点乘

    (Σk−1)T∗μk .∗ μk:(p,1)T(p,1)=(1,1)(\mathbf{\Sigma_k}^{-1})^T*\mu_k\ .*\ \mu_k:(p,1)^T(p,1)=(1,1)

    将矩阵乘转换为点乘:

    (Σk−1)T∗μk .∗ μk=sum(Σk−1.∗μk.∗μk)(\mathbf{\Sigma_k}^{-1})^T*\mu_k\ .*\ \mu_k=sum(\Sigma_k^{-1}.*\mu_k.*\mu_k)

对于一些细节的补充:

  1. 为啥必须为矩阵乘来连接两个部分,举例如下:

    若:X=(x1,x2,…,xn)X=(x_1,x_2,\dots,x_n)

    (Ax1,Ax2,…,Axn)=A∗(x1,x2,…,xn)=A∗X(Ax_1,Ax_2,\dots,Ax_n)=A*(x_1,x_2,\dots,x_n)=A*X

    而点乘则不满足这条性质:

    (A.∗x1,A.∗x2,…,A.∗xn)∗(x1,x2,…,xn)=A∗X(A.*x_1,A.*x_2,\dots,A.*x_n)*(x_1,x_2,\dots,x_n)=A*X

  2. 为啥情况五中,不允许出现点乘?

    fifth={(Σ1−1)T∗(μ.∗μ1),(Σ2−1)T∗(μ2.∗μ2),…,(Σk−1)T∗(μk.∗μk)}fifth=\{(\mathbf{\Sigma_1}^{-1})^T*(\mu_.*\mu_1),(\mathbf{\Sigma_2}^{-1})^T*(\mu_2.*\mu_2),\dots,(\mathbf{\Sigma_k}^{-1})^T*(\mu_k.*\mu_k)\}

    根据向量化的定理,根本没办法凑出一个完整的矩阵如μ\mu,若直接去除下标则为:

    (Σ−1)T∗μ.∗μ:(p,k)T(p,k)=(k,k)(\Sigma^{-1})^T*\mu.*\mu:(p,k)^T(p,k)=(k,k)

    上式肯定不可能办到,因为向量化同一个小标只能往一个方向扩展,即应为(1,k)

繁